package com.rephilo.dubbo.filter;

import com.rephilo.dubbo.filter.model.TimeMonitor;
import org.apache.dubbo.common.constants.CommonConstants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.rpc.*;

import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * dubbo方法耗时统计
 *
 * @author rephilo
 */
@Activate(group = CommonConstants.CONSUMER)
public class TPMonitorFilter implements Filter {
    private static final Long MINUTE = 60 * 1000L;
    /**
     * 记录的时间集合
     */
    private static final Map<String, List<TimeMonitor>> STATISTICS_MAP = new ConcurrentHashMap<>();
    /**
     * 定时输出统计结果
     */
    private static final ScheduledExecutorService SCHEDULED_POOL = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors());

    static {
        //提交定时输出统计结果的任务
        SCHEDULED_POOL.scheduleWithFixedDelay(TPMonitorFilter::outputTimeMonitor, 5, 5, TimeUnit.SECONDS);
    }

    /**
     * 拦截器方法
     *
     * @param invoker
     * @param invocation
     * @return
     * @throws RpcException
     */
    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        long startTime = System.currentTimeMillis();
        String methodName = invocation.getMethodName();
        try {
            return invoker.invoke(invocation);
        } finally {
            long endTime = System.currentTimeMillis();
            recordTimeMonitor(methodName, startTime, endTime);
        }
    }

    /**
     * 记录时间
     *
     * @param methodName
     * @param startTime
     * @param endTime
     */
    private void recordTimeMonitor(String methodName, long startTime, long endTime) {
        STATISTICS_MAP.putIfAbsent(methodName, new CopyOnWriteArrayList());
        STATISTICS_MAP.get(methodName).add(new TimeMonitor(startTime, endTime));
    }

    /**
     * 输出各方法统计结果
     */
    private static void outputTimeMonitor() {
        if (!STATISTICS_MAP.isEmpty()) {
            //计算当前要输出时间范围
            final Long currentEndTime = System.currentTimeMillis();
            final Long currentStartTime = currentEndTime - MINUTE;

            for (Map.Entry<String, List<TimeMonitor>> entry : STATISTICS_MAP.entrySet()) {
                List<TimeMonitor> monitorList = entry.getValue();
                if (!monitorList.isEmpty()) {
                    //过滤不在输出范围内的记录
                    List<TimeMonitor> range = monitorList.stream()
                            .filter(x -> x.getStartTime() >= currentStartTime && x.getEndTime() <= currentEndTime)
                            .sorted(Comparator.comparing(TimeMonitor::getExcuteTime))
                            .collect(Collectors.toList());
                    if (!range.isEmpty()) {
                        //计算 90% 和 99%的执行时间
                        int tp90Index = (int) Math.ceil(range.size() * 0.9) - 1;
                        int tp99Index = (int) Math.ceil(range.size() * 0.99) - 1;

                        System.out.println(entry.getKey() + " 执行时间：" + "TP90：" + range.get(tp90Index).getExcuteTime() + ", TP99：" + range.get(tp99Index).getExcuteTime());
                    }
                }
            }
        }
    }
}
